package com.xjve.framework.hibernate.query.core;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xjve.framework.hibernate.query.exception.SqlQueryException;
import org.hibernate.Session;
import org.hibernate.query.NativeQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.*;
import java.util.stream.Collectors;

/**
 * SQL查询DAO，提供基于模板的SQL查询功能
 */
public class SqlQueryDao {

    private static final Logger logger = LoggerFactory.getLogger(SqlQueryDao.class);

    // 批量处理大小
    private static final int BATCH_SIZE = 100;

    @PersistenceContext
    private EntityManager entityManager;

    private final TemplateEngine templateEngine;
    private final ParameterValidator parameterValidator;
    private final ObjectMapper objectMapper;

    public SqlQueryDao(TemplateEngine templateEngine, ParameterValidator parameterValidator, ObjectMapper objectMapper) {
        this.templateEngine = templateEngine;
        this.parameterValidator = parameterValidator;
        this.objectMapper = objectMapper;
    }

    /**
     * 执行SQL查询，返回实体类集合
     * @param templateName SQL模板名称
     * @param parameters 参数
     * @param resultType 返回类型
     * @param <T> 泛型
     * @return 实体类集合
     */
    public <T> List<T> queryFromTemplate(String templateName, Map<String, Object> parameters, Class<T> resultType) {
        Assert.hasText(templateName, "Template name must not be empty");
        Assert.notNull(resultType, "Result type must not be null");

        try {
            // 1. 编译SQL模板
            String sql = templateEngine.compile(templateName, parameters);
            logger.debug("Compiled SQL: {}", sql);

            // 2. 参数安全检查
            parameterValidator.validateParameters(sql, parameters);

            // 3. 执行查询
            Session session = entityManager.unwrap(Session.class);
            NativeQuery<Object[]> query = session.createNativeQuery(sql);

            // 4. 设置参数
            setParameters(query, parameters);

            // 5. 执行查询并处理结果
            return processQueryResults(query.getResultList(), resultType);

        } catch (Exception e) {
            throw new SqlQueryException("Failed to execute query template: " + templateName, e);
        }
    }

    /**
     * 执行SQL更新操作
     * @param templateName SQL模板名称
     * @param parameters 参数
     * @return 影响行数
     */
    public int executeFromTemplate(String templateName, Map<String, Object> parameters) {
        Assert.hasText(templateName, "Template name must not be empty");

        try {
            // 1. 编译SQL模板
            String sql = templateEngine.compile(templateName, parameters);
            logger.debug("Executing SQL: {}", sql);

            // 2. 参数安全检查
            parameterValidator.validateParameters(sql, parameters);

            // 3. 执行更新
            Session session = entityManager.unwrap(Session.class);
            NativeQuery<?> query = session.createNativeQuery(sql);

            // 4. 设置参数
            setParameters(query, parameters);

            // 5. 执行并返回影响行数
            return query.executeUpdate();

        } catch (Exception e) {
            throw new SqlQueryException("Failed to execute update template: " + templateName, e);
        }
    }

    /**
     * 查询记录总数
     * @param templateName SQL模板名称
     * @param parameters 参数
     * @return 记录总数
     */
    public int countFromTemplate(String templateName, Map<String, Object> parameters) {
        Assert.hasText(templateName, "Template name must not be empty");

        try {
            // 1. 编译SQL模板
            String sql = templateEngine.compile(templateName, parameters);
            logger.debug("Count SQL: {}", sql);

            // 2. 参数安全检查
            parameterValidator.validateParameters(sql, parameters);

            // 3. 包装为count查询
            String countSql = "SELECT COUNT(*) FROM (" + sql + ") AS count_table";

            // 4. 执行查询
            Session session = entityManager.unwrap(Session.class);
            NativeQuery<?> query = session.createNativeQuery(countSql);

            // 5. 设置参数
            setParameters(query, parameters);

            // 6. 获取结果
            Object result = query.getSingleResult();
            return result != null ? ((Number) result).intValue() : 0;

        } catch (Exception e) {
            throw new SqlQueryException("Failed to count from template: " + templateName, e);
        }
    }

    /**
     * 设置查询参数
     * @param query 查询对象
     * @param parameters 参数
     */
    private void setParameters(NativeQuery<?> query, Map<String, Object> parameters) {
        if (parameters == null || parameters.isEmpty()) {
            return;
        }

        for (Map.Entry<String, Object> entry : parameters.entrySet()) {
            String paramName = entry.getKey();
            Object value = entry.getValue();

            // 支持命名参数
            if (paramName.startsWith(":")) {
                query.setParameter(paramName.substring(1), value);
            } else {
                query.setParameter(paramName, value);
            }
        }
    }

    /**
     * 处理查询结果
     * @param results 查询结果
     * @param resultType 返回类型
     * @param <T> 泛型
     * @return 实体类集合
     */
    private <T> List<T> processQueryResults(List<Object[]> results, Class<T> resultType) {
        if (results == null || results.isEmpty()) {
            return Collections.emptyList();
        }

        List<T> resultList = new ArrayList<>();

        // 分批次处理结果
        for (int i = 0; i < results.size(); i += BATCH_SIZE) {
            int end = Math.min(i + BATCH_SIZE, results.size());
            List<Object[]> batch = results.subList(i, end);

            // 获取列名
            List<String> columnNames = getColumnNames(results);

            // 处理批次
            List<T> batchResults = processBatch(batch, columnNames, resultType);
            resultList.addAll(batchResults);
        }

        return resultList;
    }

    /**
     * 获取列名
     * @param results 查询结果
     * @return 列名列表
     */
    private List<String> getColumnNames(List<Object[]> results) {
        // 这里简化处理，实际应该从ResultSetMetadata获取列名
        // 由于Hibernate的限制，这里需要通过其他方式获取列名
        // 实际项目中可能需要使用JDBC直接查询获取元数据
        return Collections.emptyList();
    }

    /**
     * 处理批次结果
     * @param batch 批次数据
     * @param columnNames 列名
     * @param resultType 返回类型
     * @param <T> 泛型
     * @return 实体类列表
     */
    private <T> List<T> processBatch(List<Object[]> batch, List<String> columnNames, Class<T> resultType) {
        // 如果是简单类型，直接转换
        if (isSimpleType(resultType)) {
            return batch.stream()
                    .map(row -> convertSimpleType(row[0], resultType))
                    .collect(Collectors.toList());
        }

        // 如果是实体类，转换为JSON再转换为对象
        return batch.stream()
                .map(row -> convertToEntity(row, columnNames, resultType))
                .collect(Collectors.toList());
    }

    /**
     * 检查是否为简单类型
     * @param type 类型
     * @return 是否为简单类型
     */
    private boolean isSimpleType(Class<?> type) {
        return type.isPrimitive() ||
                type == String.class ||
                Number.class.isAssignableFrom(type) ||
                type == Boolean.class ||
                type == Date.class;
    }

    /**
     * 转换简单类型
     * @param value 值
     * @param targetType 目标类型
     * @param <T> 泛型
     * @return 转换后的值
     */
    private <T> T convertSimpleType(Object value, Class<T> targetType) {
        if (value == null) {
            return null;
        }

        if (targetType.isInstance(value)) {
            return targetType.cast(value);
        }

        // 简单类型转换
        try {
            if (targetType == String.class) {
                return targetType.cast(value.toString());
            } else if (Number.class.isAssignableFrom(targetType)) {
                return targetType.cast(convertNumber((Number) value, targetType));
            } else if (targetType == Boolean.class) {
                return targetType.cast(convertBoolean(value));
            } else if (targetType == Date.class) {
                return targetType.cast(convertDate(value));
            }
        } catch (Exception e) {
            logger.error("Failed to convert value {} to type {}", value, targetType.getName(), e);
        }

        throw new SqlQueryException("Unsupported type conversion from " +
                value.getClass().getName() + " to " + targetType.getName());
    }

    /**
     * 转换数字类型
     * @param number 数字
     * @param targetType 目标类型
     * @return 转换后的数字
     */
    private Number convertNumber(Number number, Class<?> targetType) {
        if (targetType == Integer.class || targetType == int.class) {
            return number.intValue();
        } else if (targetType == Long.class || targetType == long.class) {
            return number.longValue();
        } else if (targetType == Double.class || targetType == double.class) {
            return number.doubleValue();
        } else if (targetType == Float.class || targetType == float.class) {
            return number.floatValue();
        } else if (targetType == Short.class || targetType == short.class) {
            return number.shortValue();
        } else if (targetType == Byte.class || targetType == byte.class) {
            return number.byteValue();
        }
        return number;
    }

    /**
     * 转换布尔类型
     * @param value 值
     * @return 布尔值
     */
    private Boolean convertBoolean(Object value) {
        if (value instanceof Boolean) {
            return (Boolean) value;
        } else if (value instanceof Number) {
            return ((Number) value).intValue() != 0;
        } else if (value instanceof String) {
            return "true".equalsIgnoreCase((String) value) || "1".equals(value);
        }
        return false;
    }

    /**
     * 转换日期类型
     * @param value 值
     * @return 日期
     */
    private Date convertDate(Object value) {
        if (value instanceof Date) {
            return (Date) value;
        } else if (value instanceof Long) {
            return new Date((Long) value);
        }
        throw new SqlQueryException("Unsupported date conversion from " + value.getClass().getName());
    }

    /**
     * 转换为实体类
     * @param row 行数据
     * @param columnNames 列名
     * @param resultType 结果类型
     * @param <T> 泛型
     * @return 实体类对象
     */
    private <T> T convertToEntity(Object[] row, List<String> columnNames, Class<T> resultType) {
        try {
            // 创建Map存储键值对
            Map<String, Object> map = new HashMap<>();

            // 如果没有列名信息，使用默认列名
            if (columnNames == null || columnNames.isEmpty()) {
                for (int i = 0; i < row.length; i++) {
                    map.put("col" + (i + 1), row[i]);
                }
            } else {
                for (int i = 0; i < Math.min(row.length, columnNames.size()); i++) {
                    map.put(columnNames.get(i), row[i]);
                }
            }

            // 转换为JSON字符串
            String json = objectMapper.writeValueAsString(map);

            // 转换为实体类
            return objectMapper.readValue(json, resultType);

        } catch (JsonProcessingException e) {
            throw new SqlQueryException("Failed to convert result to entity: " + resultType.getName(), e);
        }
    }
}
